package com.is2.patchinglife.manager;

import java.io.File;
import java.util.Hashtable;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import android.widget.Toast;

import com.is2.patchinglife.activity.LoginActivity;
import com.is2.patchinglife.activity.MainMenuActivity;
import com.is2.patchinglife.activity.RankActivity;
import com.is2.patchinglife.wrapper.GotTrophy;
import com.turbomanage.httpclient.AsyncCallback;
import com.turbomanage.httpclient.HttpResponse;
import com.turbomanage.httpclient.ParameterMap;
import com.turbomanage.httpclient.android.AndroidHttpClient;


/*
 * httpclient' javadoc:  http://javadoc.basic-http-client.googlecode.com/git/docs/index.html 
 */


/** this class contains the methods to interact with the server **/

public class RemoteDataManager {
	private static String baseURL = "http://patchinglife.altervista.org/";
	
	
	/** Check if an active connection (wifi/3G) is active, if not ask to connect**/
	static public boolean isOnline(final Activity _caller) {
		//verify active connection
	    ConnectivityManager cm = (ConnectivityManager) LocalDataManager.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
	    NetworkInfo netInfo = cm.getActiveNetworkInfo();
	    if (netInfo != null && netInfo.isConnectedOrConnecting()) {
	        return true;
	    }
	    
	    // ask to connect
	    AlertDialog.Builder builder=new AlertDialog.Builder(_caller);
  		builder.setTitle("ALERT");
  		builder.setMessage("An Internet connection is required for this functionality. "+
  						   "Do you want to open the Settings Menu?");
  		builder.setCancelable(true);

  		// if user click this, open system settings
  		builder.setPositiveButton("SETTINGS", new DialogInterface.OnClickListener() {
  				
  			public void onClick(DialogInterface dialog, int which) {

  				Intent settings_intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
  				_caller.startActivity(settings_intent);
  				
  			}
  		});
      
  		//if user click this, do nothing
  		builder.setNegativeButton("CANCEL", null);
  		
  		// create & show dialog
  		AlertDialog alert=builder.create();
  		alert.show();
	    return false;
	}

	
	/** Get the Trophy list in JSON from the server.**/
	
	static public void getInfoTrophyList(){
		 // create a new httpClient to baseurl
		 AndroidHttpClient httpClient = new AndroidHttpClient(baseURL);
		 	// set the parameter to pass to the server
	        ParameterMap params = httpClient.newParams()
	                .add("do", "getInfoTrophyList");

	        // set timeout 10s
	        httpClient.setConnectionTimeout(10000);
	        httpClient.setReadTimeout(10000);
	        
	        // do an HTTP GET request to the server & set callback
	        httpClient.get("index.php", params, new AsyncCallback() {
				
				public void onComplete(HttpResponse httpResponse) {
					String tmpString =  httpResponse.getBodyAsString();
					//if response has no error write update the info_trophy
					if(httpResponse != null && !tmpString.equals("") && tmpString.substring(0,1).equals("{") && !tmpString.equals("ERROR")){
						//TODO: maybe we should check if response is Valid JSON before write it
						// update file and parse it to update the istanced object
	        			LocalDataManager.setInfoTrophy(LocalDataManager.infoTrophyFromJSON(tmpString));
	        			LocalDataManager.saveInfoTrophy();
	        			
					}else{
						//show alert dialog
						showErrorDialog("An error with server is occurred. Your trophy information could not be updated.","LoginActivity");
						Log.e("remoteDataManager","getTrophyList has error");
					}
					
				}
				
				@Override
				public void onError(Exception ex){
					ex.printStackTrace();
					Log.e("remoteDataManager","getTrophyList HTTP error");
					try{
						ActivityManager.getInstance().get("LoginActivity");
						showErrorDialog("An error with server is occurred. Your trophy information could not be updated.","LoginActivity");
					}catch(Exception e){
						showErrorDialog("An error with server is occurred. Your trophy information could not be updated.","MainMenuActivity");	
					}
					
				}
			});
	}

	/**  Try To login the user to the server  **/
	static public void authenticate(final String _user,String _psw){

		//create a new httpClient to baseurl
		 AndroidHttpClient httpClient = new AndroidHttpClient(baseURL);
	 	//set the parameter to pass to the server
	    ParameterMap params = httpClient.newParams()
            .add("u",_user)
            .add("p", _psw);
        httpClient.setConnectionTimeout(5000);	//set Timeout 5s
        httpClient.setReadTimeout(5000);	//Set Timeout 5s
        httpClient.setMaxRetries(3);			//Set num of retry

        //send Cookie value, must be clean so we don't have parsing error
		httpClient.addHeader("Cookie","; path=/");
		
		//do an HTTP POST request (login is the server function to invoke) and set Callback
        httpClient.post("index.php?do=login",params,new AsyncCallback() {
        
			public void onComplete(HttpResponse httpResponse) {
				String tmpString = null;
				if(httpResponse!=null){
					tmpString = httpResponse.getBodyAsString();
				}
				// Check if server first characters are "Error", if so check the type and handle it
				if(tmpString==null || ((tmpString.length()>5) && (tmpString.substring(0,5).equals("ERROR"))  )){

					//toggle visibility for ProgressBar & Login form
					((LoginActivity) ActivityManager.getInstance().get("LoginActivity")).toggleLoading();
					
					//Check Error type and show correct message
					if(tmpString==null){
						showErrorDialog("An Error occurred while receiving server response. Please try again.","LoginActivity");
					}else if(tmpString.equals("ERROR_NOUSER")){
						showErrorDialog("Username cannot be empty.","LoginActivity");
					}else if(tmpString.equals("ERROR_NOPASSWORD")){
						showErrorDialog("Password cannot be empty.","LoginActivity");
					}else if(tmpString.equals("ERROR_USERNOTFOUND")){
						showErrorDialog("Invalid username and/or password.","LoginActivity");
					}else{
						showErrorDialog("An unexpected error occurred. Please Retry.","LoginActivity");
					}
							
					
				// User authenticated correctly
				}else if(tmpString.equals("SUCCESS")){
					
					// Save username in property 'cookie' of GUEST config file and save it,
					// so when we open the app we can read it at start (the config file is 
					//read always at start and eventually replaced) and know that there is
					//a logged user
					LocalDataManager.getConf().setCookieValue(_user);
					LocalDataManager.saveConf(null);
				
					//delete eventually shown notification
					NotificationManager notificationManager = (NotificationManager) LocalDataManager.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
					notificationManager.cancelAll();
					// Check if an old config Exists and load it else create a new one
					File configfile = new File("conf"+_user+".json");
					if(configfile.exists()){
						// read already existing config
						LocalDataManager.parseConf(_user); 

					//create a new config file
					}else{
						//get the cookie value from the HTTP Response and set it in the config Object
						String _cookieName = httpResponse.getHeaders().get("set-cookie").get(0);
						int start = _cookieName.indexOf("=")+1;
						int end = _cookieName.indexOf(";");
						_cookieName = _cookieName.substring(start, end);
						LocalDataManager.getConf().setCookieValue(_cookieName );
						LocalDataManager.getConf().setUser(_user);
						//save the config object as file
						LocalDataManager.saveConf(_user);
					}
					//LocalDataManager.parseGotTrophy(username);
					
					// Upload user Trophy & get the updated list
					syncUserTrophy(true);

					// Update list of all Trophies
	        		getInfoTrophyList();
				}
			}
			
			@Override
			public void onError(Exception ex){
				ex.printStackTrace();
				Log.e("remoteDataManager","Authenticate HTTP error");
				showErrorDialog("An unexpected error occurred. Please Retry.","LoginActivity");
				((LoginActivity) ActivityManager.getInstance().get("LoginActivity")).toggleLoading();
			}
		});
	}
	
	
	
	/** Try to subscribe the user to server 
	 * _user: username to register with
	 * _psw: user's password
	 * _name: user's name // not fully utilized
	 * importTrophy: if the guest's Trophy will be imported (and deleted) to the subscribing user
	 * **/
	static public void userSubscribe(final String _user,final String _psw, String _name,final boolean _importTrophy){
		//create a new httpClient to baseurl
		 AndroidHttpClient httpClient = new AndroidHttpClient(baseURL);
	 	//set the parameter to pass to the server
		ParameterMap params = httpClient.newParams()
				.add("u",_user)
				.add("p", _psw)
				.add("n",_name);

		httpClient.setConnectionTimeout(6000); 		//set timeout 6s
		httpClient.setReadTimeout(6000); 		//set timeout 6s
		httpClient.setMaxRetries(3);				//set retries
		httpClient.addHeader("Cookie","; path=/");	// set cookie
		//do an HTTP POST request (userSubscribe is the server function to invoke) and set Callback
		httpClient.post("index.php?do=userSubscribe", params,new AsyncCallback() {

			public void onComplete(HttpResponse httpResponse) {
				
				//Check if server subscription is successful  and automatically login
				
				if(httpResponse != null){
					String body = httpResponse.getBodyAsString();
					
					if(body != null && body.compareTo("SUCCESS") == 0){
						String _cookieName = null;
						try{
						//get the value of cookie
						 _cookieName = httpResponse.getHeaders().get("set-cookie").get(0);
						int start = _cookieName.indexOf("=")+1;
						int end = _cookieName.indexOf(";");
						_cookieName = _cookieName.substring(start, end);
						}catch(Exception e){
							
							try{
								//get the value of cookie
							
							 _cookieName = httpResponse.getHeaders().get("Set-Cookie").get(0);
							int start = _cookieName.indexOf("=")+1;
							int end = _cookieName.indexOf(";");
							_cookieName = _cookieName.substring(start, end);
							}catch(Exception e1){
								
							}
						}
						
						// Save username in property 'cookie' of GUEST config file and save it,
						// so when we open the app we can read it at start (the config file is 
						// read always at start and eventually replaced) and know that there is
						// a logged user
						if(_cookieName != null){
							LocalDataManager.getConf().setCookieValue(_user);
							LocalDataManager.saveConf(null);
						
							// set username and cookievalue in config e save it to file
							LocalDataManager.getConf().setUser(_user);
							LocalDataManager.getConf().setCookieValue(_cookieName );
							LocalDataManager.saveConf(_user);
						}else{
							showErrorDialog("User registration Complete but the app couldn't login, try manually", "LoginActivity");
						}
						//read the template for user's trophy file.
						String gotTemplate = LocalDataManager.getFileManager().readFromAssets("got_trophy.json");
						
						//Import Guest Trophies 
						if(_importTrophy){
							//Save Guest Trophy as New user's Trophy
							LocalDataManager.saveGotTrophy(_user);
							
							//reset Guest Trophies
							LocalDataManager.getFileManager().write(gotTemplate, "got_trophy.json");
							
							//Update
							syncUserTrophy();
						
						//do not import 
						}else{
							//save Guest Trophy to file
							LocalDataManager.saveGotTrophy(null);
							//Save new Trophy file for user
							LocalDataManager.getFileManager().write(gotTemplate, "got_trophy"+_user+".json");
							// Parse file just created
							LocalDataManager.parseGotTrophy(_user);
						}
						
						//delete eventually shown notification
						NotificationManager notificationManager = (NotificationManager) LocalDataManager.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
						notificationManager.cancelAll();
						
	
						// Start MainMenu activity and close every other one
						Activity callee = ActivityManager.getInstance().get("SignUpActivity");
						Intent main_intent = new Intent(callee,MainMenuActivity.class);
						
						String[] aList = {"ProfileActivity","ConfigActivity","LoginActivity","MainMenuActivity","TrophyActivity","RankActivity"};
						ActivityManager.getInstance().closeActivities(aList, true);
						
						callee.startActivity(main_intent);
						callee.finish();
						
					//Server returned an error,  handle it
					}else if(body==null){
						showErrorDialog("Connection Error, retry.","SignUpActivity");
						
					}else if(body.compareTo("ERROR_NODATA") == 0){
						showErrorDialog("Connection Error, retry.","SignUpActivity");
					
					}else if(body.compareTo("ERROR_USER_ALREADY_EXIST") == 0){
						showErrorDialog("Username "+_user+" already exists. Choose another one.","SignUpActivity");
						
					}else if(body.compareTo("ERROR_FAILED_FOR_UNKNOWN_REASON") == 0){
						showErrorDialog("An Error occurred. Please try again.","SignUpActivity");
						Log.e("SignUp","Server Error on SignUP \n"+httpResponse.getBodyAsString());
						
					}else{
						Log.e("SignUp","Error on SignUP \n"+httpResponse.getBodyAsString());
					}
					
				}else {
					showErrorDialog("An Error occurred. Please try again.","SignUpActivity");
					Log.e("SignUp","An Error occurred. httpResponse is null.");
					
				}
			}
			@Override
			public void onError(Exception ex){
				ex.printStackTrace();
				Log.e("remoteDataManager","Authenticate HTTP error");
				showErrorDialog("An Error occurred. Please try again.","SignUpActivity");
				
			}
		});
	}

	/* Upload the JSON of currently known obtained Trophy to the server and get the full list of */
	static public void syncUserTrophy(){
		syncUserTrophy(false);
	}
	
	/** Upload the JSON of currently known obtained Trophy to the server and get the full list of
	 *  If is sync on login, close every other activity to prevent user from go back
	 **/
	static public void syncUserTrophy(final boolean isFirstLogin){
		String toSend = LocalDataManager.gotTrophyToJSON();
		
		//create a new httpClient to baseurl
		 AndroidHttpClient httpClient = new AndroidHttpClient(baseURL);
	 	//set the parameter to pass to the server
		ParameterMap params = httpClient.newParams()
				.add("GotTrophy",toSend);
		//get cookie value and add it as a header
		httpClient.addHeader("Cookie",LocalDataManager.getConf().getCookieName()+"="+LocalDataManager.getConf().getCookieValue()+"; path=/");

		httpClient.setConnectionTimeout(10000);	//set timeout 10s
		httpClient.setReadTimeout(10000);
		//do an HTTP POST request (upGotTrophy is the server function to invoke) and set Callback
		httpClient.post("index.php?do=upGotTrophy", params,new AsyncCallback() {

			public void onComplete(HttpResponse httpResponse) {
				String tmpString = null;
				if(httpResponse!=null){
					tmpString = httpResponse.getBodyAsString();
				}			
				
				//check if server return an error, handle it
				if(tmpString == null || (tmpString.length() < 1) || ((tmpString.length() > 5) && (tmpString.substring(0,5).equals("ERROR")))){
					showSyncAlert(isFirstLogin);
				//check if response is the start of JSON string
				}else if(tmpString.substring(0,1).equals("{")){
					//try to parse server response 
					try{
						Hashtable<String,GotTrophy> parsed = LocalDataManager.gotTrophyFromJSON(httpResponse.getBodyAsString());
						LocalDataManager.setGotTrophy(parsed);
						LocalDataManager.saveGotTrophy(LocalDataManager.getConf().getUser());
						
						/* if is first login Close every other activity to prevent user to go back to old screens **/
						if(isFirstLogin){
							
							String[] aList = {"ProfileActivity","ConfigActivity","MainMenuActivity","TrophyActivity","SignUpActivity","RankActivity"};
							ActivityManager.getInstance().closeActivities(aList, true);
							
							//start MainMenu
							Activity callee = ActivityManager.getInstance().get("LoginActivity");
							
							Intent main_intent = new Intent(callee,MainMenuActivity.class);
							callee.startActivity(main_intent);
							
							//close Login Activity
							callee.finish();	
						}else{
							Toast toast = Toast.makeText(LocalDataManager.getContext(), "Synchronization complete", Toast.LENGTH_SHORT);
							toast.show();
						}
						
					}catch(Exception e){
						// show an alert dialog
						showSyncAlert(isFirstLogin);
					
					
					}
				}else{
					Log.e("getGotTrophy","error");
				}
			}
			@Override
			public void onError(Exception ex){
				ex.printStackTrace();
				Log.e("remoteDataManager","SYNC HTTP error");
				showSyncAlert(isFirstLogin);
			}
		});
	}
	
	/** Exec necessary operation for logout **/
	static public void userLogout(){
		//save config and gotTrophy file
		LocalDataManager.saveConf(LocalDataManager.getUser());
		LocalDataManager.saveGotTrophy(LocalDataManager.getUser());
		
		// logout operation
		//get Guest config & gotTrophy
		LocalDataManager.parseConf(null);
		LocalDataManager.parseGotTrophy(null);
		//reset cookieValue 
		LocalDataManager.getConf().setCookieValue("");
		LocalDataManager.getConf().setUser("GUEST");
		LocalDataManager.saveConf(null);

		//delete eventually shown notification
		NotificationManager notificationManager = (NotificationManager) LocalDataManager.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
		notificationManager.cancelAll();
		
		//Facebook operation
		FacebookManager.logoutFacebook();
		
		// go to login and close every other activity to prevent user to go back
		Activity caller = ActivityManager.getInstance().get("ProfileActivity");
		Intent main_intent = new Intent(caller,LoginActivity.class);
		
		String[] aList = {"ConfigActivity","LoginActivity","MainMenuActivity","TrophyActivity","SignUpActivity","RankActivity"};
		ActivityManager.getInstance().closeActivities(aList, true);
		
		caller.startActivity(main_intent);
		caller.finish();
	}
	
	/** Error Dialog**/
	static private void showErrorDialog(String _mex, String _activity){
		Activity caller = ActivityManager.getInstance().get( _activity);
		AlertDialog.Builder builder=new AlertDialog.Builder(caller);
		builder.setTitle("ALERT");
		builder.setMessage(_mex);
		builder.setCancelable(true);
		builder.setPositiveButton("OK", null);
		AlertDialog alert=builder.create();
		alert.show();
	}
	
	
	/*****************************************
	 * Rank functions
	 *****************************************/
	
	/** Get a page of the rank of user with more Points **/
	static public void getUserRank(int _pages){
		getUserRank(_pages,"Points");
	}
	
	/** Get a page of the rank of user
	 * _rankType can be "Points" or "Trophy" 
	 **/
	
	static public void getUserRank(int _pages,String _rankType){
		//create an HTTP client
		AndroidHttpClient httpClient = new AndroidHttpClient(baseURL);
		//Add cookie
		httpClient.addHeader("Cookie",LocalDataManager.getConf().getCookieName()+"="+LocalDataManager.getConf().getCookieValue()+"; path=/");
		httpClient.setConnectionTimeout(5000); //set timeout, 5s
		httpClient.setReadTimeout(5000); 		//set timeout 6s
		//add params
		ParameterMap params  = httpClient.newParams();
		params.add("page",String.valueOf(_pages)); 	//current page
		params.add("by",_rankType);					//rank type
		params.add("do","getUserRank");				//function (rank)
		
		//do HTTP GET request & set callback
		httpClient.get("index.php",params ,new AsyncCallback() {

			@Override
			public void onComplete(HttpResponse httpResponse) {
				String tmpString = null;
				if(httpResponse!=null){
					tmpString = httpResponse.getBodyAsString();
				}			
				
				//check if server return an error, handle it
				if(tmpString == null || (tmpString.length() < 5) || ((tmpString.length() > 5) && (tmpString.substring(0,5).equals("ERROR")))){
					//Error Handling
					showErrorDialog("An error occurred, please retry",  "RankActivity");
					Log.e("getUserRank","ERROR - server responce: \n "+tmpString);
				}else if(tmpString.substring(0,1).equals("{")){
					//Richiama il metodo per la costruzione della lista su schermo
					RankActivity a = (RankActivity) ActivityManager.getInstance().get("RankActivity");
					if(a != null){
						a.drawRankList(tmpString,"userRank");
					}else{
						Log.e("getUserRank","ERROR - Activity destroyed before execute: \n "+tmpString);
					}
				}else{
					//Some unknow error occurred
					showErrorDialog("An error occurred, please retry",  "RankActivity");
					Log.e("getUserRank","ERROR - server responce: \n "+tmpString);
				}
			}
			@Override
			public void onError(Exception ex){
				ex.printStackTrace();
				Log.e("remoteDataManager","GetUserRank Error");
				showErrorDialog("An error occurred, please retry",  "RankActivity");
			}
		});

	}
	/*
	 * get the rank of more obtained Trophies 
	 */
	static public String getTrophyRank(int _pages){

		//Create a new HTTP client
		AndroidHttpClient httpClient = new AndroidHttpClient(baseURL);
		//add cookie
		httpClient.addHeader("Cookie",LocalDataManager.getConf().getCookieName()+"="+LocalDataManager.getConf().getCookieValue()+"; path=/");
		httpClient.setConnectionTimeout(5000);	//set Timeout
		httpClient.setReadTimeout(5000); 		//set timeout 5s
		//set request Params
		ParameterMap params  = httpClient.newParams();
		params.add("page",String.valueOf(_pages));
		params.add("do","getTrophyRank");

		//do an HTTP GET request and set callback
		httpClient.get("index.php",params ,new AsyncCallback() {

			@Override
			public void onComplete(HttpResponse httpResponse) {
				
				String tmpString = httpResponse.getBodyAsString();
				if((tmpString.length() > 5) && (tmpString.substring(0,5).equals("ERROR"))){
					//Error Handling
					showErrorDialog("An error occurred, please retry",  "RankActivity");
					Log.e("getUserRank","ERROR - server responce: \n "+tmpString);

				}else if(tmpString.substring(0,1).equals("{")){
					RankActivity a = (RankActivity) ActivityManager.getInstance().get("RankActivity");
					a.drawRankList(tmpString,"trophyRank");
				}else{
					//Error Handling
					showErrorDialog("An error occurred, please retry",  "RankActivity");
					Log.e("getTrophyRank","ERROR - server responce: \n "+tmpString);
				}
			}
			@Override
			public void onError(Exception ex){
				ex.printStackTrace();
				Log.e("remoteDataManager","GetUserRank Error");
				showErrorDialog("An error occurred, please retry",  "RankActivity");
			}
		});
		return null;
	}
	

	
	/** Alert Dialog to display in case of Sync Error
	 * - positive button: retry the sync
	 * - negative button,
	 *  **/
	static private void showSyncAlert( final boolean isFirstLogin){
		Activity caller = null;
		
		if(!isFirstLogin){
			caller = ActivityManager.getInstance().get("MainMenuActivity");	
			
		}else{
			caller = ActivityManager.getInstance().get("LoginActivity");
			
		}
		//get the alert builder
		AlertDialog.Builder builder=new AlertDialog.Builder(caller);

		//set some param
		builder.setTitle("Errore");
		builder.setMessage("An Error occured while synching your trophies.");
		builder.setCancelable(true);

		//set callback for positive button
		builder.setPositiveButton("Retry",new OnClickListener(){
			
				public void onClick(DialogInterface dialog, int which) {
					//retry the sync
					RemoteDataManager.syncUserTrophy(isFirstLogin);
				}
		});
		//set callback for negative button
		builder.setNegativeButton("Ignore", new OnClickListener() {
			
			@Override
			public void onClick(DialogInterface dialog, int which) {
				String user = LocalDataManager.getUser();
				
				//if user gotTrophy file does not exists, the we must create it (first login)
				File gotTrophyFile = new File("got_trophy"+user+".json");
				if(!gotTrophyFile.exists()){
					//Set a basic gotTrophy Template
					
					String gotTemplate = LocalDataManager.getFileManager().readFromAssets("got_trophy.json");
					LocalDataManager.getFileManager().write(gotTemplate, "got_trophy"+user+".json");
				}
				//create & show an alert
				if(!isFirstLogin){
					Activity caller = ActivityManager.getInstance().get("MainMenuActivity");	
					AlertDialog.Builder builder=new AlertDialog.Builder(caller);
					builder.setTitle("Avviso");
					builder.setMessage("Try to sync the trophy later tapping \"Synchronize\" .");
					builder.setPositiveButton("OK",null);
					builder.show();
				}else{
					LoginActivity caller = (LoginActivity) ActivityManager.getInstance().get("LoginActivity");
					// simulate logout operation
					//get Guest config & gotTrophy
					LocalDataManager.parseConf(null);
					LocalDataManager.parseGotTrophy(null);
					//reset cookieValue 
					LocalDataManager.getConf().setCookieValue("");
					LocalDataManager.getConf().setUser("GUEST");
					LocalDataManager.saveConf(null);
					caller.toggleLoading();
					
				}
			
			}
		});
		AlertDialog alert=builder.create();
		alert.show();
	}
}